//-----------------------------------------------------------------------------
// File: Win32ToolTipServer.cs
//
// Copyright (c) Dmitry Shuklin. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;


namespace ObjectiveDraw.Platform.Misc
{
	/// <summary>
	/// Summary description for Win32ToolTipServer.
	/// </summary>
	public class Win32ToolTipServer: Cerebrum.Direct3D.Forms.Control, Cerebrum.Direct3D.Forms.IToolTipServer
	{
		MessageBalloon m_mb;

		public Win32ToolTipServer()
		{
			//
			// TODO: Add constructor logic here
			//
			m_mb = new MessageBalloon();
			this.SetVisibleCore(false);
		}

		#region IToolTipServer Members

		public void ShowMessage(Cerebrum.Direct3D.Forms.Control control, System.Drawing.PointF point, string value)
		{
			Point m_point = Point.Round(point);
			System.Windows.Forms.Control parent = control.FindDevice().RenderTarget;
			m_point= parent.PointToScreen(m_point); 
			m_mb.Text = value;		
		
			m_mb.CenterStem = false;
			m_mb.UseAbsolutePositioning = true ;			
			m_mb.Show(parent, m_point.X,m_point.Y);		
		}

		public void HideMessage(Cerebrum.Direct3D.Forms.Control control)
		{
			m_mb.Hide();
		}

		#endregion		

	}
	

	
	public enum TooltipIcon : int
	{
		None,
		Info,
		Warning,
		Error
	}


	

	public delegate void DeActivateEventHandler();

	internal class MessageTool : NativeWindow
	{
		private const int WM_LBUTTONDOWN = 0x0201;
		public event DeActivateEventHandler DeActivate;
		
		protected override void WndProc(ref System.Windows.Forms.Message m)
		{
			if( m.Msg == WM_LBUTTONDOWN )
			{
				System.Diagnostics.Debug.WriteLine(m);
				// allow the balloon to close if clicked upon
				if(DeActivate!=null)
				{
					DeActivate();
				}
			}

			base.WndProc(ref m);
		}
	}

	public class MessageBalloon : IDisposable
	{	
		private MessageTool m_tool = null;
		private TOOLINFO ti;

		private int m_maxWidth = 250;
		private string m_text = "";
		
		
		private bool m_absPosn = false;
		private bool m_centerStem = false;

		private const string TOOLTIPS_CLASS = "tooltips_class32";
		private const int WS_POPUP = unchecked((int)0x80000000);
		private const int WS_EX_TOPMOST	= (int)0x00000008;
		private const int WM_USER = 0x0400;
		private readonly IntPtr HWND_TOPMOST = new IntPtr(-1);		
		private const int SWP_NOSIZE = 0x0001;
		private const int SWP_NOMOVE = 0x0002;
		private const int SWP_NOACTIVATE = 0x0010;
		private const int SWP_NOZORDER = 0x0004;
		

		/*private const int HWND_TOP        = 0;
		private const int HWND_BOTTOM     = 1;
		private const int HWND_TOPMOST    = -1;
		private const int HWND_NOTOPMOST  = -2;*/

		[DllImport("User32", SetLastError=true)]
		private static extern int SetWindowPos(
			IntPtr hWnd,
			IntPtr hWndInsertAfter,
			int X,
			int Y,
			int cx,
			int cy,
			int uFlags);

		[DllImport("User32", SetLastError=true)]
		private static extern int GetClientRect(
			IntPtr hWnd,
			ref RECT lpRect);

		[DllImport("User32", SetLastError=true)]
		private static extern int ClientToScreen(
			IntPtr hWnd,
			ref RECT lpRect);

		[DllImport("User32", SetLastError=true)]
		private static extern int SendMessage(
			IntPtr hWnd,
			int Msg,
			int wParam,
			IntPtr lParam);

		[StructLayout(LayoutKind.Sequential)]
			private struct RECT
		{
			public int left;
			public int top;
			public int right;
			public int bottom;
		}

		private const int TTS_ALWAYSTIP = 0x01;
		private const int TTS_NOPREFIX = 0x02;
		private const int TTS_BALLOON = 0x40;
		private const int TTS_CLOSE = 0x80;
		
		private const int TTM_TRACKPOSITION = WM_USER + 18;
		private const int TTM_SETMAXTIPWIDTH = WM_USER + 24;
		private const int TTM_TRACKACTIVATE = WM_USER + 17;
		private const int TTM_ADDTOOL = WM_USER + 50;
		private const int TTM_SETTITLE = WM_USER + 33;

		private const int TTF_IDISHWND = 0x0001;
		private const int TTF_SUBCLASS = 0x0010;
		private const int TTF_TRACK = 0x0020;
		private const int TTF_ABSOLUTE = 0x0080;
		private const int TTF_TRANSPARENT = 0x0100;
		private const int TTF_CENTERTIP = 0x0002;
		private const int TTF_PARSELINKS = 0x1000;

		[StructLayout(LayoutKind.Sequential)]
			private struct TOOLINFO
		{
			public int cbSize;
			public int uFlags;
			public IntPtr hwnd;
			public IntPtr uId;
			public RECT rect;
			public IntPtr hinst;
			[MarshalAs(UnmanagedType.LPTStr)] 
			public string lpszText;
			public uint lParam;
		}
		
		/// <summary>
		/// Creates a new instance of the MessageBalloon.
		/// </summary>
		public MessageBalloon()
		{
			
			m_tool = new MessageTool();
			m_tool.DeActivate += new DeActivateEventHandler(this.Hide);
		}

	

		~MessageBalloon()
		{
			Dispose(false);
		}

		private bool disposed = false;
		public void Dispose()
		{
			Dispose(true);
			// Take yourself off the Finalization queue 
			// to prevent finalization code for this object
			// from executing a second time.
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if(!this.disposed)
			{
				if(disposing)
				{
					// release managed resources if any
				}
				
				// release unmanaged resource
				Hide();

				// Note that this is not thread safe.
				// Another thread could start disposing the object
				// after the managed resources are disposed,
				// but before the disposed flag is set to true.
				// If thread safety is necessary, it must be
				// implemented by the client.
			}
			disposed = true;
		}

	
		private void CreateTool(System.Windows.Forms.Control control, int X, int Y)
		{

			//			System.Diagnostics.Debug.Assert(
			//				m_parent.Handle!=IntPtr.Zero, 
			//				"parent hwnd is null", "SetToolTip");

			CreateParams cp = new CreateParams();
			cp.Parent = control.Handle;
			cp.ClassName = TOOLTIPS_CLASS;
			cp.Style = 
				WS_POPUP | 
				TTS_BALLOON | 
				TTS_NOPREFIX |	
				TTS_ALWAYSTIP |
				TTS_CLOSE;

			cp.ExStyle = WS_EX_TOPMOST;
			// create the tool
			m_tool.CreateHandle(cp);

			// create and fill in the tool tip info
			ti = new TOOLINFO();
			ti.cbSize = Marshal.SizeOf(ti);

			ti.uFlags = TTF_TRACK |
				TTF_IDISHWND |
				TTF_TRANSPARENT | 
				TTF_SUBCLASS |
				TTF_PARSELINKS;

			// absolute is used tooltip maynot be shown 
			// if coords exceed the corners of the screen
			if(m_absPosn)
			{
				ti.uFlags |= TTF_ABSOLUTE;
			}


			if(m_centerStem)
			{
				ti.uFlags |= TTF_CENTERTIP;
			}

			ti.uId = m_tool.Handle;
			ti.lpszText = m_text;
			ti.hwnd = control.Handle;

			//			GetClientRect(m_parent.Handle, ref ti.rect);
			//			ClientToScreen(m_parent.Handle, ref ti.rect);
			//
			//			// make sure we make it the top level window
			//			SetWindowPos(
			//				m_tool.Handle, 
			//				HWND_TOPMOST, 
			//				0, 0, 0, 0,
			//				SWP_NOACTIVATE | 
			//				SWP_NOMOVE | 
			//				SWP_NOSIZE);

			// add the tool tip
			IntPtr ptrStruct = Marshal.AllocHGlobal(Marshal.SizeOf(ti));
			Marshal.StructureToPtr(ti, ptrStruct, true);

			SendMessage(
				m_tool.Handle, TTM_ADDTOOL, 0, ptrStruct);

			ti = (TOOLINFO)Marshal.PtrToStructure(ptrStruct, 
				typeof(TOOLINFO));

			SendMessage(
				m_tool.Handle, TTM_SETMAXTIPWIDTH, 
				0, new IntPtr(m_maxWidth));
			
			SetBalloonPosition(X,Y);
		
			Marshal.FreeHGlobal(ptrStruct);
			//	Marshal.FreeHGlobal(ptrTitle);
		}



		private void SetBalloonPosition(int x, int y)
		{						

			SetWindowPos(
				m_tool.Handle, 
				HWND_TOPMOST, 
				x, y, 0, 0,
				SWP_NOACTIVATE | 
				SWP_NOSIZE);

			int pt = MAKELONG(x, y);
			IntPtr ptr = new IntPtr(pt);
			SendMessage(
				m_tool.Handle, TTM_TRACKPOSITION,
				0, ptr);
		}


		/// <summary>
		/// Shows or hides the tool.
		/// </summary>
		/// <param name="show">0 to hide, -1 to show</param>
		private void Display(int show)
		{
			IntPtr ptrStruct = Marshal.AllocHGlobal(Marshal.SizeOf(ti));
			try
			{
				Marshal.StructureToPtr(ti, ptrStruct, true);

				SendMessage(
					m_tool.Handle, TTM_TRACKACTIVATE,
					show, ptrStruct);
			}
			catch
			{
			}
			Marshal.FreeHGlobal(ptrStruct);
		}

		/// <summary>
		/// Hides the message if visible.
		/// </summary>
		public void Hide()
		{
			Display(0);
			m_tool.DestroyHandle();
		}

		private int MAKELONG(int loWord, int hiWord)
		{
			return (hiWord << 16) | (loWord & 0xffff);
		}

	

		/// <summary>
		/// Sets or get the display text.
		/// </summary>
		public string Text
		{
			get
			{
				return m_text;
			}
			set
			{
				m_text = value;
			}
		}

		

		/// <summary>
		/// Sets or gets the positioning of the balloon.
		/// TRUE : Positions using the exact co-ordinates,
		/// if the co-ordinates are outside the screen, tip wont be shown.
		/// FALSE : Positions using the co-ordinates as a reference.
		/// Regardless of the co-ordinates, the tip will 
		/// always be shown on the screen.
		/// </summary>
		public bool UseAbsolutePositioning
		{
			get
			{
				return m_absPosn;
			}
			set
			{
				m_absPosn = value;
			}
		}

		/// <summary>
		/// Sets or gets the stem position 
		/// in the tip. 
		/// TRUE : The stem of the tip is set to center.
		/// An attempt is made to show the tip with the stem
		/// centered, if that would make the tip to be 
		/// hidden partly, stem is not centered.
		/// FALSE: Stem is not centered.
		/// </summary>
		public bool CenterStem
		{
			get
			{
				return m_centerStem;
			}
			set
			{
				m_centerStem = value;
			}
		}

		
		public void Show(System.Windows.Forms.Control control, int x,int y)
		{
			// recreate window always
			Hide();
			CreateTool(control, x,y);
			Display(-1);
		}
	}
}

